<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref } from 'vue';
  import { invoke } from '@tauri-apps/api/core';
  import { writeText } from '@tauri-apps/plugin-clipboard-manager';
  import ContentModal from './content-modal.vue';
  import CategoryModal from './category-modal.vue';
  import { LogicalSize, Window } from '@tauri-apps/api/window';
  import { useDragDrop } from '../../../hooks/drag-drop.ts';
  import ToolList from './tool-list.vue';

  const currentWindow = Window.getCurrent();
  const categoryModalRef = ref();
  const contentModalRef = ref();

  const state = reactive({
    isDragging: false,
    dataList: [],
    categoryList: [],
    categoryMap: {},
    categoryTotalMap: {},
    categoryTotal: 0,
    keyword: '',
    categoryName: '',
    currentCategoryId: 'all',
    timer: null,
    windowW: 1200,
    windowH: 650,
    importTimer: null,
    synth: null as any,
    voiceMsg: null as any
  });
  const setWindowSize = () => {
    currentWindow.setSize(new LogicalSize(state.windowW, state.windowH));
  };
  const clickCategory = id => {
    state.currentCategoryId = id;
    state.keyword = '';
    if (id === 'tool') {
      return;
    }
    if (id === 'all') {
      getCodeList();
    } else {
      invoke('list_code_info_by_category', {
        categoryId: id
      }).then(res => {
        state.dataList = res;
      });
    }
  };
  const clickAddCategory = () => {
    categoryModalRef.value.openDialog();
  };
  const clickEditCategory = item => {
    categoryModalRef.value.openDialog(item);
  };
  const clickDeleteCategory = id => {
    invoke('delete_category_info', {
      id
    }).then(res => {
      if (!res) {
        window.$message?.error('分类中存在数据，不能删除');
        return false;
      }
      window.$message?.success('删除成功');
      state.currentCategoryId = null;
      getCategoryList();
    });
  };
  const clickAdd = () => {
    contentModalRef.value.openDialog();
  };
  const changeFile = (event) => {
    if (state.importTimer) {
      clearTimeout(state.importTimer);
    }
    state.importTimer = setTimeout(() => {
      const file = event.file.file;
      if (!file) return;
      if (!file.type !== 'application/json' && !file.name.endsWith('.json')) {
        window.$message?.error('请选择json文件');
        return;
      }
      const reader = new FileReader();
      reader.onload = e => {
        try {
          const data = JSON.parse(e.target.result);
          if (file.name.indexOf('category') > -1) {
            const idList = state.categoryList.map(item => item.id);
            let importFlag = false;
            for (let i = 0; i < data.length; i++) {
              const item = data[i];
              if (!idList.includes(item.id)) {
                importFlag = true;
                invoke('create_category_info', {
                  ...item,
                  sort: item.sort || i + 1,
                  gmtCreate: new Date().getTime(),
                  gmtModified: new Date().getTime()
                }).then(_ => {
                  getCategoryList();
                  window.$message?.success('导入成功');
                });
              }
            }
            if (!importFlag) {
              window.$message?.success('没有需要新增的分类');
            }
          } else {
            const map = {};
            state.categoryList.map(item => {
              map[item.name] = item.id;
            });
            for (let i = 0; i < data.length; i++) {
              const item = data[i];
              invoke('create_code_info', {
                ...item,
                sort: item.sort || i + 1,
                id: null,
                categoryId: map[item.category_name],
                categoryName: item.category_name,
                gmtCreate: new Date().getTime(),
                gmtModified: new Date().getTime()
              }).then(_ => {
                getCodeList();
                window.$message?.success('导入成功');
              }).catch(err => {
                window.$message?.error('导入失败' + err);
              });
            }
          }
        } catch (err) {
          window.$message?.error(err);
        }
      };
      reader.readAsText(file);
    }, 1000);
  };
  const clickExport = () => {
    invoke('list_code_info', {
      keyword: ''
    }).then(res => {
      const data = JSON.stringify(res, null, 2);
      const blob = new Blob([data], { type: 'application/json' });
      const aTag = document.createElement('a');
      aTag.href = URL.createObjectURL(blob);
      aTag.download = `code-${new Date().getTime()}.json`;
      document.body.appendChild(aTag);
      aTag.click();
      document.body.removeChild(aTag);
      URL.revokeObjectURL(aTag.href);
    });
    const categoryData = JSON.stringify(state.categoryList, null, 2);
    const categoryBlob = new Blob([categoryData], { type: 'application/json' });
    const categoryATag = document.createElement('a');
    categoryATag.href = URL.createObjectURL(categoryBlob);
    categoryATag.download = `category-${new Date().getTime()}.json`;
    document.body.appendChild(categoryATag);
    categoryATag.click();
    document.body.removeChild(categoryATag);
    URL.revokeObjectURL(categoryATag.href);
  };
  const clickVolume = item => {
    cancelVoice();
    state.voiceMsg = new SpeechSynthesisUtterance();
    state.voiceMsg.text = item.title + '\n' + item.code.replace(/!\[Description\]\([^)]*\)/g, '');
    state.voiceMsg.lang = 'zh-Cn';
    state.voiceMsg.rate = 1.5;
    state.voiceMsg.pitch = 1;
    state.synth = window.speechSynthesis;
    state.synth.speak(state.voiceMsg);
  };
  const cancelVoice = () => {
    if (state.synth) {
      state.synth.cancel(state.voiceMsg);
    }
  };
  const clickEdit = item => {
    contentModalRef.value.openDialog(item);
  };
  const clickDelete = id => {
    invoke('delete_code_info', {
      id
    }).then(res => {
      window.$message?.success('删除成功');
      refreshCode();
    });
  };
  const getCategoryList = () => {
    invoke('list_category_info', {
      name: state.categoryName,
    }).then(res => {
      state.categoryList = res;
      (state.categoryList || []).map(item => {
        state.categoryMap[item.id] = item.name;
      });
    });
  };
  const getCategoryTotal = () => {
    invoke('get_code_count_by_category').then(res => {
      state.categoryTotalMap = res;
      let total = 0;
      for (const o in state.categoryTotalMap) {
        total += state.categoryTotalMap[o];
      }
      state.categoryTotal = total;
    });
  }
  const getCodeList = () => {
    invoke('list_code_info', {
      keyword: state.keyword,
    }).then(res => {
      state.dataList = res;
      state.currentCategoryId = 'all';
      getCategoryTotal();
    });
  };
  const searchCode = () => {
    if (state.timer) {
      clearTimeout(state.timer);
    }
    state.timer = setTimeout(() => {
      getCodeList();
    }, 300);
  };
  const searchCategoryList = () => {
    if (state.timer) {
      clearTimeout(state.timer);
    }
    state.timer = setTimeout(() => {
      getCategoryList();
    }, 300);
  }
  const clickCopy = code => {
    writeText(code).then(res => {
      window.$message.success('复制成功');
    });
  };
  const clickHide = () => {
    currentWindow.hide();
  };
  const refreshCode = () => {
    clickCategory(state.currentCategoryId);
    getCategoryTotal();
  };
  const clickCatalogueItem = index => {
    const targetEle = document.getElementById('data-item-' + index);
    targetEle?.scrollIntoView({
      behavior: 'smooth'
    });
  }
  const { startDrag, doDrag, endDrag} = useDragDrop();

  onMounted( () => {
    document.addEventListener('mousemove', doDrag);
    document.addEventListener('mouseup', endDrag);
    setWindowSize();
    getCodeList();
    getCategoryList();
    getCategoryTotal();
  });
  onUnmounted(() => {
    document.removeEventListener('mousemove', doDrag);
    document.removeEventListener('mouseup', endDrag);
  });
</script>

<template>
  <div class="content-wrapper">
    <div class="operate-bar p2">
      <div class="name" @mousedown="startDrag">
        代码管理工具 <span class="font-size-12px color-#ccc">(左键按住此处拖动)</span>
      </div>
      <div class="window-btn">
        <n-space>
          <n-button size="small" text @click="clickHide">
            <img class="w-15px" src="@/assets/svg-icon/close.svg" alt="" title="隐藏到托盘">
          </n-button>
        </n-space>
      </div>
    </div>
    <div class="top-wrapper flex justify-between items-center p2">
      <n-space class="w-100px">
        <n-button size="small" text @click="clickAdd">
          <img class="w-15px" src="@/assets/svg-icon/add.svg" alt="" title="添加">
        </n-button>
        <n-upload action="/" accept=".json" :show-file-list="false" @change="changeFile">
          <n-button size="small" text>
            <img class="w-15px" src="@/assets/svg-icon/import.svg" alt="" title="导入">
          </n-button>
        </n-upload>
        <n-button size="small" text @click="clickExport">
          <img class="w-15px" src="@/assets/svg-icon/export.svg" alt="" title="导出">
        </n-button>
      </n-space>
      <div class="w100%">
        <n-input size="small" placeholder="请输入" v-model:value="state.keyword" clearable @input="searchCode"></n-input>
      </div>
    </div>
    <n-split style="height: calc(100vh - 90px);" :min="0.2" :max="0.5" default-size="220px">
      <template #1>
        <div class="left-wrapper bg-#fff">
          <div class="p2 left-box">
            <div class="flex">
              <n-input size="small" placeholder="请输入分类名称" v-model:value="state.categoryName" clearable @input="searchCategoryList"></n-input>
              <n-button size="small" text @click="clickAddCategory">
                <img class="ml1 w-15px" src="@/assets/svg-icon/add.svg" alt="" title="添加分类">
              </n-button>
            </div>
            <n-scrollbar style="height: calc(100% - 44px);">
              <div class="category-list">
                <div :class="['category-item', state.currentCategoryId === 'tool' ? 'active' : '']" @click="clickCategory('tool')">工具类</div>
                <div :class="['category-item', state.currentCategoryId === 'all' ? 'active' : '']" @click="clickCategory('all')">
                  全部
                  <n-tag class="total" :bordered="false" type="info">
                    {{state.categoryTotal}}
                  </n-tag>
                </div>
                <div :class="['category-item', item.id === state.currentCategoryId ? 'active' : '']"
                     v-for="item in state.categoryList" :key="item.id" @click="clickCategory(item.id)">
                  {{item.name}}
                  <n-tag class="total" :bordered="false" type="success">
                    {{state.categoryTotalMap[item.id]}}
                  </n-tag>
                  <n-button size="small" text class="update-btn" @click="clickEditCategory(item)">
                    <img title="编辑分类" src="@/assets/svg-icon/edit.svg" alt="" />
                  </n-button>
                  <n-button size="small" text class="delete-btn" @click="clickDeleteCategory(item.id)">
                    <img title="删除" src="@/assets/svg-icon/delete.svg" alt="" />
                  </n-button>
                </div>
              </div>
            </n-scrollbar>
          </div>
        </div>
      </template>
      <template #2>
        <div class="ml-2px bg-#fff data-list" v-if="state.currentCategoryId !== 'tool'">
          <n-scrollbar v-if="state.dataList.length > 0">
            <div class="catalogue-btn">
              <div class="catalogue-title">目录</div>
              <div class="catalogue-wrapper">
                <n-scrollbar style="max-height: 360px;">
                  <div class="catalogue-item" v-for="(item, index) in state.dataList" :key="item.id" @click="clickCatalogueItem(index)">
                    {{index + 1}}、{{item.title}}
                  </div>
                </n-scrollbar>
              </div>
            </div>
            <div :id="'data-item-' + index" class="item p2" v-for="(item, index) in state.dataList" :key="item.id">
              <div class="fw700">{{index + 1}}、{{ item.title }}【{{state.categoryMap[item.category_id]}}】</div>
              <div :class="['high-box', index !== state.dataList.length - 1 ? 'border-line' : '']">
                <v-md-preview :text="item.code"></v-md-preview>
                <n-button size="small" text class="copy-btn" @click="clickCopy(item.code)">
                  <img title="复制" src="@/assets/svg-icon/copy.svg" alt="">
                </n-button>
                <n-button size="small" text class="volume-btn" @click="clickVolume(item)">
                  <img title="播放" src="@/assets/svg-icon/volume-on.svg" alt=""></img>
                </n-button>
                <n-button size="small" text class="volume-off-btn" @click="cancelVoice">
                  <img title="关闭" src="@/assets/svg-icon/volume-off.svg" alt=""></img>
                </n-button>
                <n-button size="small" text class="update-btn" @click="clickEdit(item)">
                  <img title="编辑" src="@/assets/svg-icon/edit.svg" alt="" />
                </n-button>
                <n-button size="small" text class="delete-btn" @click="clickDelete(item.id)">
                  <img title="删除" src="@/assets/svg-icon/delete.svg" alt="" />
                </n-button>
              </div>
            </div>
          </n-scrollbar>
          <n-empty v-else class="mt40" description="暂无数据" />
        </div>
        <ToolList v-else class="flex-1 ml-2px bg-#fff data-list" />
      </template>
    </n-split>
    <CategoryModal ref="categoryModalRef" @refresh-list="getCategoryList"></CategoryModal>
    <ContentModal ref="contentModalRef" @refresh-list="refreshCode" :category-list="state.categoryList"/>
  </div>
</template>

<style scoped lang="scss">
  .content-wrapper{
    height: 100%;
    .operate-bar{
      border-bottom: 1px solid #f2f2f2;
      display: flex;
      justify-content: space-between;
      align-items: center;
      .name{
        user-select: none;
      }
      .window-btn{
        .n-button{
          &:hover{
            background: #f2f2f2;
          }
        }
      }
    }
    .top-wrapper{
      border-bottom: 1px solid #f2f2f2;
    }
    .left-wrapper{
      height: calc(100vh - 90px);
      .left-box{
        height: calc(100% - 16px);
        .category-list{
          .category-item{
            padding-top: 12px;
            padding-bottom: 12px;
            padding-right: 52px;
            cursor: pointer;
            border-bottom: 1px solid #f2f2f2;
            position: relative;
            &.active{
              color: #126ac6;
            }
            .total{
              position: absolute;
              top: 12px;
              right: 20px;
            }
            img{
              width: 15px;
            }
            .update-btn{
              position: absolute;
              top: 12px;
              right: 4px;
            }
            .delete-btn{
              position: absolute;
              top: 28px;
              right: 4px;
            }
          }
        }
      }
    }
    .data-list{
      height: calc(100vh - 90px);
      .catalogue-btn{
        .catalogue-title{
          position: fixed;
          top: 84px;
          right: 12px;
          z-index: 999;
          cursor: pointer;
          padding: 8px;
        }
        &:hover{
          color: #126ac6;
          .catalogue-wrapper{
            display: block;
          }
        }
        .catalogue-wrapper{
          display: none;
          position: absolute;
          top: 28px;
          right: 16px;
          background: #fff;
          z-index: 999;
          box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
          .catalogue-item {
            padding: 8px;
            color: #000;
            cursor: pointer;
            &:hover{
              background: rgba(0, 0, 0, .1);
            }
          }
        }
      }
      .high-box{
        position: relative;
        &.border-line{
          border-bottom: 2px solid #ccc;
        }
        img{
          width: 15px;
        }
        .copy-btn{
          position: absolute;
          top: 8px;
          right: 8px;
        }
        .volume-btn{
          position: absolute;
          bottom: 8px;
          right: 59px;
        }
        .volume-off-btn{
          position: absolute;
          bottom: 8px;
          right: 42px;
        }
        .update-btn{
          position: absolute;
          bottom: 8px;
          right: 8px;
        }
        .delete-btn{
          position: absolute;
          bottom: 8px;
          right: 25px;
        }
      }
    }
  }
</style>
